
using System;
using System.Collections.Generic;
using System.Text;

namespace ScrewTurn.Wiki {

	/// <summary>
	/// Manages the Wiki's Recent Changes.
	/// </summary>
	public class RecentChanges {

		private static RecentChanges instance;

		/// <summary>
		/// Gets or sets the singleton instance of the RecentChanges object.
		/// </summary>
		public static RecentChanges Instance {
			get {
				if(instance == null) throw new InvalidOperationException("RecentChanges.Instance is null");
				return instance;
			}
			set { instance = value; }
		}

		private List<RecentChange> changes;
		private int threshold = 50; // The maximum number of changes
		private int cutSize = 20; // The number of changes that are removed when the threshold is reached

		/// <summary>
		/// Initializes a new instance of the <b>RecentChanges</b> class.
		/// </summary>
		public RecentChanges() {
			changes = new List<RecentChange>(threshold);
			Load();
		}

		private void Load() {
			lock(changes) {
				changes.Clear();

				// Load from file
				string data = Tools.LoadFile(Settings.RecentChangesFile).Replace("\r", "");
				string[] lines = data.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);

				string[] fields;
				for(int i = 0; i < lines.Length; i++) {
					try {
						// Try/catch to avoid problems for corrupted file (raw method)
						fields = lines[i].Split('|');
						Change c = Change.PageUpdated;
						switch(fields[4].ToUpperInvariant()) {
							case "U":
								c = Change.PageUpdated;
								break;
							case "D":
								c = Change.PageDeleted;
								break;
							case "R":
								c = Change.PageRolledBack;
								break;
							case "N":
								c = Change.PageRenamed;
								break;
						}
						changes.Add(new RecentChange(fields[0], fields[1].Replace("&#124;", "|"), DateTime.Parse(fields[2]), fields[3], c, fields.Length == 6 ? Tools.UnescapeString(fields[5]) : ""));
					}
					catch { }
				}
			}
		}

		/// <summary>
		/// Gets all the Changes (not sorted).
		/// </summary>
		public RecentChange[] AllChanges {
			get {
				lock(changes) {
					return changes.ToArray();
				}
			}
		}

		/// <summary>
		/// Adds a new change.
		/// </summary>
		/// <param name="page">The Page Name.</param>
		/// <param name="title">The Page Title.</param>
		/// <param name="dateTime">The Date/Time.</param>
		/// <param name="user">The User.</param>
		/// <param name="change">The Change.</param>
		/// <param name="descr">The Description (optional).</param>
		public void AddChange(string page, string title, DateTime dateTime, string user, Change change, string descr) {
			lock(changes) {
				changes.Add(new RecentChange(page, title, dateTime, user, change, descr));
				StringBuilder sb = new StringBuilder();
				sb.Append("\r\n");
				sb.Append(page);
				sb.Append("|");
				sb.Append(title.Replace("|", "&#124;"));
				sb.Append("|");
				sb.Append(dateTime.ToString("yyyy'/'MM'/'dd' 'HH':'mm':'ss"));
				sb.Append("|");
				sb.Append(user);
				sb.Append("|");
				switch(change) {
					case Change.PageUpdated:
						sb.Append("U");
						break;
					case Change.PageDeleted:
						sb.Append("D");
						break;
					case Change.PageRolledBack:
						sb.Append("R");
						break;
					case Change.PageRenamed:
						sb.Append("N");
						break;
				}
				if(descr.Length > 0) {
					sb.Append("|");
					sb.Append(Tools.EscapeString(descr));
				}
				Tools.AppendFile(Settings.RecentChangesFile, sb.ToString());
				if(changes.Count >= threshold) {
					CutSize(changes.Count - cutSize);
				}
			}
		}

		/// <summary>
		/// Reduces the size of the "log" to the specified size, deleting old entries.
		/// </summary>
		/// <param name="size">The new Size.</param>
		public void CutSize(int size) {
			lock(changes) {
				if(size >= changes.Count) return;

				int idx = changes.Count - size + 1;

				RecentChange[] ch = AllChanges;

				StringBuilder sb = new StringBuilder();

				for(int i = idx; i < ch.Length; i++) {
					sb.Append(ch[i].Page);
					sb.Append("|");
					sb.Append(ch[i].Title.Replace("|", "&#124;"));
					sb.Append("|");
					sb.Append(ch[i].DateTime.ToString("yyyy'/'MM'/'dd' 'HH':'mm':'ss"));
					sb.Append("|");
					sb.Append(ch[i].User);
					sb.Append("|");
					switch(ch[i].Change) {
						case Change.PageUpdated:
							sb.Append("U");
							break;
						case Change.PageDeleted:
							sb.Append("D");
							break;
						case Change.PageRolledBack:
							sb.Append("R");
							break;
						case Change.PageRenamed:
							sb.Append("N");
							break;
					}
					if(i != ch.Length - 1) sb.Append("\r\n");
				}
				Tools.WriteFile(Settings.RecentChangesFile, sb.ToString());
				Load();
			}
		}

	}

}
